home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48hor1 / rpl3.doc < prev    next >
Text File  |  1995-03-31  |  14KB  |  312 lines

  1. (Comp.sys.handhelds) 
  2. Item: 2282 by bson at rice-chex.ai.mit.edu 
  3. Author: [Jan Brittenson] 
  4.   Subj: Some HP-48 Internals Answers 
  5.   Date: Fri Mar 01 1991 
  6.  
  7.    Joe Ervin asked me some questions in a letter. It seemed to me that 
  8. the questions were fundamental enough to be answered on the net - I 
  9. haven't seen any in-depth discussions of these topics before; I hope 
  10. Joe will excuse me, and the rest of you bear with me. If you hate 
  11. everything related to HP-48 internals, hit the proper Junk key now. I 
  12. will stick to STAR syntax and AG mnemonics. 
  13.  
  14.  > In all the examples of system RPL I have seen, I have NOT seen any 
  15.  > loops or IF-THEN constructs, although I believe that Bill Wickes has 
  16.  > IF-THEN in his ASC routines. 
  17.  
  18.    I recently posted a set of FFT-aiding routines here on 
  19. comp.sys.handhelds. Two of them use if-then constructs, and one has a 
  20. loop as well as local variables. The latter took quite some exper- 
  21. imentation to get right! 
  22.  
  23.    Here is one of them, it collapses an array into a one-dimensional 
  24. list (I have reduced it to bare bones here - let's not bother with the 
  25. actual addresses): 
  26.  
  27.  
  28.         RPL 
  29.           Prg                   ; PROGRAM 
  30.             SaveLast_Need_1_arg ;   Housekeeping stuff 
  31.             ArrayTo_array       ;   Explode array 
  32.             ListAlgPrgTo        ;   Explode dimensions list 
  33.             Equal2p 
  34.             If_then_else        ;   Then: (real->short) * (real->short) 
  35.             Prg 
  36.               Real_to_short_Swap 
  37.               Real_to_short 
  38.               Mul 
  39.             End 
  40.             Real_to_short       ;   Else: real->short 
  41.             ToList              ;   Combine to list 
  42.           End                   ; END 
  43.         ENDRPL 
  44.  
  45.  
  46. Now, broken down step by step: 
  47.  
  48.  
  49. RPL     Enters STAR RPL mode. 
  50.  
  51. Prg     Start of program object (2d9d). 
  52.  
  53. SaveLast_Need_1_arg 
  54.         Makes sure we have at least one argument. 
  55.  
  56. ArrayTo_array 
  57.         Explodes the array - ARRY-> on an array. This yields a list of 
  58.         reals containing dimensional info, e.g. {2 3} for a 2-by-3 
  59.         array. 
  60.  
  61. ListAlgPrgTo 
  62.         Explodes the list - LIST-> on a list. Like the user-mode 
  63.         command, expcept the the counter on level 1 is a short. 
  64.  
  65. Equal2p If level 1 is the short 2, then return True, else False. True 
  66.         is #3a81 and False is #3ac0. These both appear as "External" 
  67.         on the stack, and can't be entered from user code. Like shorts 
  68.         (system binaries) are preferred internally for small integers, 
  69.         such as the list count above, True and False is the system RPL 
  70.         preferred way of passing around boolean flags. 
  71.  
  72. If_then_else (#61ad8) 
  73.         If level 1 is True (#3a81) then execute the next object, 
  74.         otherwise execute the second next object. 
  75.  
  76. Prg     Start of program object (2d9d). This program object is the 
  77.         then-clause. This gets evaluated if the list contained two 
  78.         elements, i.e. was 2-dimensional. Otherwise it was one- 
  79.         dimensional. 
  80.  
  81. Real_to_short_Swap 
  82.         (Then-clause) Convert a real (from the array dimensions list) 
  83.         to a short and do a swap. 
  84.  
  85. Real_to_short 
  86.         (Then-clause) Convert the second real to a short. 
  87.  
  88. Mul     (Then-clause) Multiply the two shorts. Notice that we could 
  89.         just as well have multiplied the two reals (using the user- 
  90.         code real multiplication function) and then converted the 
  91.         result to a short. But short (20-bit unsigned integer) 
  92.         mulitplication is magnitudes faster than real (64-bit BCD) 
  93.         multiplication. 
  94.  
  95. End     End (#312b) of the then-clause program object. 
  96.  
  97. Real_to_short 
  98.         This is the else-clause. The array was 1-dimensional, so 
  99.         we just convert the size to a short. 
  100.  
  101. ToList  Finally we put it all back to a list. The item count on 
  102.         level 1 is assumed to be a short. 
  103.  
  104. End     This is the end of our program, and the end of the object. 
  105.  
  106. ENDRPL  Signals end of STAR RPL mode. 
  107.  
  108.  
  109.  > It looks like in the system RPL, you cannot simply push an object 
  110.  > onto the stack the way you do in user code. 
  111.  
  112.    Sure you can. System RPL executes almost identically to user RPL. 
  113. The big difference is that you can't automatically interrupt it with 
  114. ATTN - the system RPL has to explicitly check for this. System RPL 
  115. also has a lot of constructs for implementing control and branch 
  116. functions - you can, for instance, write the If_then_else above in 
  117. system RPL by calling more primitive functions like Skip (which skips 
  118. the next token in the *caller's* thread), or pick the next token from 
  119. the caller's thread, or evaluate the token in the caller's thread. A 
  120. token here is either an object or the 20-bit address of an object. In 
  121. fact, If_then_else *is* written in system RPL if I remember correctly. 
  122. There are more specialized examples; we could, for instance, have 
  123. written an If_equal2p_then_else function if we wanted (and had the 
  124. option of putting it in a fixed location in ROM!). 
  125.  
  126.  
  127.  > This is apparantly because system RPL consists strictly (I think) of 
  128.  > nibble addresses.  Because of this, you can't really put an object 
  129.  > into the thread, because the "threader", or whatever you call the code 
  130.  > which does the JUMP @(next rpl token) won't know what to do with an 
  131.  > object it wasn't expecting. 
  132.  
  133.    A token is always either a pointer to a _type_ or an object, in 
  134. which case its first subtoken, the prefix, is a type. There is never 
  135. more than one level of indirection here. The types (#2d92 for 
  136. programs, #2a2c for strings, #2dcc for code, etc) all begin with 
  137. #28fc, which indicates that "this is a type." The code at #28fc 
  138. implements the semantics of the _type_. What it mostly does is to call 
  139. the type-dependent code implementing the type. I have to confess I am 
  140. on very weak ice here, so don't take this as the ultimate truth, it's 
  141. merely based on my observations. It's fairly clear that what most data 
  142. types do, is to simply push themselves on the stack. Then there are 
  143. others (lists and symbolics) that sometimes push themselves on the 
  144. stack, and sometimes behave as programs. Finally there are those that 
  145. always result in code being executed - programs (#2d9d) and code 
  146. (#2dcc). Then we have global and local names that evaluate their 
  147. values. 
  148.  
  149. I think that exactly how this works is best illustrated by an example. 
  150.  
  151. The code at #28fc looks like this: 
  152.  
  153.                 move.p2 10, c 
  154.                 sub.a   c, a 
  155.                 jump    @a 
  156.  
  157. Assume that the next token is a program object. Then the sequence: 
  158.  
  159.                 move.a  @d0, a 
  160.                 add     5, d0 
  161.                 jump    @a 
  162.  
  163.    Will 1) put the prefix (#2d9d) of the program in A. 2) Advance the 
  164. pointer (which now points immediately following the prefix). 3) Read 
  165. the contents of #2d9d (which is the "type" prefix #28fc) and jump 
  166. there (i.e. to #28fc). Upon arriving at #28fc, A will contain #2d9d. 
  167. 10 is subtracted from it (the #28fc "type" prefix at #2d9d is preceded 
  168. by two addresses that implement certain semantics of "programs") to 
  169. yield the address of a pointer to the Eval-specific semantics for 
  170. "programs," i.e. a piece of ML code that does whatever "programs" are 
  171. supposed to do when they are executed (EVALed). This is where it then 
  172. jumps. D0 still points to the word following the prefix, and A is 
  173. #2d9d minus 10. 
  174.  
  175.    As to the composition of RPL objects, I would recommend you to take 
  176. a look at one of Derek Nickel's postings which explicitly addresses 
  177. this. It should suffice to mention here that program, symbolic, and 
  178. list objects are almost identical apart from the prefixes. 
  179.  
  180.  
  181.  > There are some RPL tokens that require an argument, and the cases I 
  182.  > have seen of this always have the argument appear as the next token. 
  183.  > This is the case for the "type_check" token which appeared in your 
  184.  > recent example code, and I strongly suspect that this works with some 
  185.  > other tokens. 
  186.  
  187.    Quite correct. Sometimes it's even more complicated, like the type 
  188. check routines that take any paired sequence of short tokens and 
  189. tokens to be executed if a match was made; up to an end (#312b). If a 
  190. match is made the matching process ends and the subsequent token is 
  191. executed. Otherwise the current address (in D0) is advanced to the 
  192. next pair. When it reaches an end (#312b) a "bad argument type" error 
  193. is issued. Some of these type matching functions also play tricks 
  194. with the LAST ARG data. 
  195.  
  196.  
  197.  > When I started looking at system RPL, I was of the belief that the 
  198.  > system RPL would be a lot like the user RPL. This does not appear to 
  199.  > be true. It seems that for any looping structure or IF-THEN structure, 
  200.  > or any more complicated structure, it looks like the system RPL will 
  201.  > require different techniques. 
  202.  
  203.    They are not really that much more complicated, just undocumented 
  204. and slightly different. They sometimes require knowledge of exactly 
  205. how they work to be taken full advantage of. One illustrative example 
  206. is loops. My understanding is that they basically consist of a head 
  207. and a tail. The head enters the loop (similar to START...NEXT in its 
  208. simpler form) and the tail terminates it. Now, what the head does more 
  209. exactly is to set the return address *to itself* and call the first 
  210. word of the loop. The end of the loop basically does an END, which 
  211. causes it to return to the loop head and reexecute it! Since a loop 
  212. call frame looks like any other call frame, the possibilities are 
  213. almost endless for implementing you own specialized NEXT, or STEP, or 
  214. UNTIL, or whatever your heart may desire. The tail can exit the loop 
  215. by unwinding one call frame level and continuing. 
  216.  
  217.    You can, if you like, implement a specialized NEXT (or other loop 
  218. controls) and put it in a global variable. You can even implement 
  219. specialized IFs, skips, what may you desire, as well, by putting them 
  220. in a global variables. A small example (make sure your HP-48 is set to 
  221. HEX!): 
  222.  
  223.  
  224. Key in PUSH (ASC format) and de-ASC it: 
  225.  
  226. "CCD20310008F146608DC2016C1CD" 
  227.  
  228.  
  229. Type:           #61ad8 #5a03 SYSEVAL    @ --> <61ad8> 
  230.                 PUSH                    @ --> External 
  231.                 'IFFTE' STO 
  232.  
  233.    Now we have stored the If_then_else routine (#61ad8) in IFFTE, or 
  234. more precisely, a pointer to it. Type in the following program (notice 
  235. that True (#3a81) and False (#3ac0) push themselves on the stack when 
  236. evaluated): 
  237.  
  238.                 << IFFTE "TRUE" "FALSE" >> 'X' STO 
  239.  
  240.  
  241.    Then let's try it out (the @#XXXX notation used means "the object 
  242. at address XXXX" - the display will actually read "External" in our 
  243. example below): 
  244.  
  245.                 #3a81 SYSEVAL           @ --> @#3a81 (True) 
  246.                 X                       @ --> "TRUE" 
  247.                 DROP 
  248.                 #3ac0 SYSEVAL           @ --> @#3ac0 (False) 
  249.                 X                       @ --> "FALSE" 
  250.  
  251.    Voila! A new user RPL control structure! Notice that our user 
  252. program X above is identical to the following (STAR syntax), with the 
  253. exception that << and >> tokens have been added to the user program; 
  254. also note that an initial "_" character escapes the implicit STAR 
  255. DATA.A of RPL mode: 
  256.  
  257.                 RPL 
  258.                   2d9d                  ; Program object 
  259.                     _global `IFFTE'     ;   Token: Global symbol object 
  260.                     _string `TRUE'      ;   Token: String object 
  261.                     _string `FALSE'     ;   Token: String object 
  262.                   312b                  ; End 
  263.                 ENDRPL 
  264.  
  265.  
  266.    For clarification, the global name and strings above are made 
  267. in-line objects, not pointers. 
  268.  
  269.  
  270.  > In fact, it appears that for many tasks, the normal user code is much, 
  271.  > much simpler, and will probably run nearly as fast. 
  272.  
  273.    Simpler because they are more well-documented, yes. But not faster 
  274. by a long shot! System RPL mostly makes type checking superfluous, 
  275. except initially, and does most of its arithmetic using 20-bit 
  276. integers. 
  277.  
  278.  > Surely there are many highly useful ROM routines that will be nice to 
  279.  > use, but aren't those generally accessible from user code using 
  280.  > SYSEVAL? 
  281.  
  282.    Yes, of course. But who can read a program consisting solely on 
  283. SYSEVALs? Binary integers and SYSEVALs take a lot more space and are 
  284. much slower, although they can be "compiled" using Joseph Horn's PACK 
  285. program. I personally tend to test algorithms and generally make 
  286. prototypes in user RPL, and then simply recode it in system RPL for 
  287. robustness, speed, and size. Not everything of course, I find it 
  288. generally desirable to code the "workhorse" routines in system RPL and 
  289. call them from user RPL that can easily be customized. 
  290.  
  291.  > One key advantage with system RPL for me is that it can hold a CODE 
  292.  > object; a feat of which user code does not appear to be capable. 
  293.  
  294.    This is most useful indeed. And while you're at it, with assemblers 
  295. and all, you may just as well do the rest in system RPL. Or at least 
  296. embed the ML in some argument checking. 
  297.  
  298.  > I will refrain from asking you anymore direct questions for now, for 
  299.  > fear that I will totally overwhelm you. 
  300.  
  301.    Au contraire; I'm glad you posed these questions. I remember 
  302. wondering over this too, and I'm sure a lot of people want to know. I 
  303. don't claim to know that much about how the HP-48 works; Rick Grevelle 
  304. and a dozen other net people probably know much more than I do. 
  305. Perhaps explaining the basics will get others started, I hope so - 
  306. which is why I spent a little extra effort on this reply (as well as 
  307. posted it to the net). I hope I am correct - I'm sure I will quickly 
  308. be corrected if I'm not! 
  309.  
  310.                                                 -- Jan Brittenson 
  311.                                                    bson@ai.mit.edu 
  312.